白屏、首屏慢如何排查与优化?
考察点
- 桌面端特有的白屏成因(与 Web 不同)
- 系统化的首屏优化手段
参考答案
1. 先分层定位:白屏发生在哪一环
主进程 ready → 创建窗口 → 加载页面 → preload 执行 → 页面 JS 执行 → 首屏渲染
① ② ③ ④ ⑤ ⑥
逐环排查:
- ①② 主进程慢:ready 前做了重活(同步 IO、模块 require 过多)
- ③ 加载慢:远程 URL 网络慢 / 本地文件被 asar 读取阻塞 / 协议 handler 慢
- ④ preload 慢或报错:同步重逻辑、报错导致桥缺失(页面依赖 api 而卡死)
- ⑤ JS 执行慢:bundle 太大、首屏依赖链太长
- ⑥ 渲染慢:首屏请求数据串行、字体/图片阻塞
2. 立即见效的手段
(1)show: false + ready-to-show(消灭"白屏观感")
const win = new BrowserWindow({
show: false, // 先不显示,避免用户看到加载过程的白屏
backgroundColor: '#1e1e1e', // 与页面底色一致,加载底色不突兀
});
win.once('ready-to-show', () => {
win.show(); // 首帧渲染完成再显示
});
(2)骨架屏/启动画面内联 窗口创建立刻加载一个极简 loading 页(内联 data URI 或独立轻量 html),主 bundle ready 后切换,用户感知从"白屏"变"启动动画"。
(3)主进程建窗时机优化
// ❌ ready 里做一堆事再建窗
app.whenReady().then(async () => {
await initDatabase(); // 耗时
await loadConfig(); // 耗时
createWindow(); // 窗口迟迟不建
});
// ✅ 先建窗(loading 页),初始化并行,完成后再通知页面
app.whenReady().then(() => {
createWindow(); // 立即显示骨架
Promise.all([initDatabase(), loadConfig()]).then(() => {
win.webContents.send('app:ready');
});
});
3. 加载链路优化
(1)资源本地化:生产环境页面、图片、字体全部本地加载(自定义协议),零网络依赖——这是桌面端相对 Web 的天然优势
(2)bundle 优化(与 Web 同理):
- 路由级代码分割,首屏只加载必要 chunk
- 依赖分析(rollup-plugin-visualizer),砍掉误入首屏的大依赖
- 首屏关键 CSS 内联
(3)preload 瘦身:preload 顶层同步代码阻塞页面加载,重活全部异步化
(4)数据加载并行化:首屏数据从"页面 mount 后串行请求"改为"主进程预取 + 页面 invoke 拿缓存"
4. 量化与监控
// 主进程记录建窗耗时
const t0 = Date.now();
win.once('ready-to-show', () => {
logger.info(`首窗耗时: ${Date.now() - t0}ms`);
});
// 渲染进程记录 Web 指标
new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
// FCP / LCP 上报
report({ name: entry.name, value: entry.startTime });
}
}).observe({ type: 'paint', buffered: true });
建立"启动耗时"基线指标,每次发版对比,防劣化。
5. 特殊场景的白屏
| 场景 | 原因 | 对策 |
|---|---|---|
| 打包后白屏(dev 正常) | 路径错误/preload 未打包/协议未注册 | 解 asar 检查、看主进程日志 |
| 偶发白屏 | 渲染进程 OOM 崩溃 | render-process-gone 监听 + 重载 |
| 唤醒后白屏 | GPU 进程异常 | disable-gpu 排查、Chromium 版本问题 |
| 高分屏白屏闪烁 | GPU 光栅化问题 | 特定硬件 --disable-gpu-rasterization 类开关兜底 |
6. 面试话术
排查先按"建窗→加载→preload→JS→渲染"五环定位。优化上,观感层用 show:false + ready-to-show + 背景色 + 骨架屏;链路层用资源本地化、preload 瘦身、主进程初始化与建窗并行、bundle 分割;最后用启动耗时埋点建基线防劣化。打包后白屏第一嫌疑人永远是路径和协议注册。
常见追问
- 骨架屏怎么和主页面切换不闪? 同窗口内 loading 页作为主页面的一部分(一个 html 里的 loading 态),而不是两个页面跳转。
- 远程 URL 场景白屏还有什么手段? 预连接(webContents 预热)、离线缓存(service worker / session 缓存)、加载超时降级页。